﻿//
// eCRAM, a monthly activity report web application
// Copyright (C) 2010  Aptea
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

using System;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Diagnostics;
using System.Linq;
using eCRAM.Model;
using System.Collections.Generic;
using System.Linq.Expressions;
using eCRAM.Model.CustomModel;
using eCRAM.Tools;

namespace eCRAM.Dao
{
    /// <summary>
    /// Allows to manipulate the CRAMs that are inside the SQL database.
    /// </summary>
    /// 
    /// <remarks>
    /// CRAM means in french Compte-Rendu d'Activité Mensuel i.e Monthly Activity Report
    /// A CRAM contains an id, a date, a state and a link to a user.
    /// Check CramModel.dbml for more informations.
    /// </remarks>
    public static class CramRepository
    {
        public static IEnumerable<Cram> GetAll()
        {
            using (var context = new eCramDataContext())
            {
                var dlo = new DataLoadOptions();
                dlo.LoadWith<Cram>(c => c.User);
                dlo.LoadWith<Cram>(c => c.CramLines);
                dlo.LoadWith<Cram>(c => c.Cram_Attachments);
                dlo.LoadWith<CramLine>(c => c.Activity);
                dlo.LoadWith<CramLine>(c => c.Cram_DailyActivities);
                dlo.LoadWith<Activity>(c => c.ActivityType);
                dlo.LoadWith<Activity>(c => c.User_Activities);
                dlo.LoadWith<User>(c => c.Activities);

                context.LoadOptions = dlo;

                return context.Crams.ToList();
            }
        }

        public static void Update(Cram cram)
        {
            using (var context = new eCramDataContext())
            {
                try
                {
                    context.Log = Console.Out;
                    LinqTools.Update(cram, c => c.Id == cram.Id, context);
                    context.SubmitChanges();
                }

                catch (ChangeConflictException)
                {
                    foreach (ObjectChangeConflict changeConflict in context.ChangeConflicts)
                    {
                        MetaTable metatable = context.Mapping.GetTable(changeConflict.Object.GetType());
                        var entityInConflict = (Cram)changeConflict.Object;

                        Console.WriteLine("Table name: {0}", metatable.TableName);
                        Console.WriteLine();
                        Console.WriteLine("Item ID: {0} ", entityInConflict.Id)
                        ;
                        Console.WriteLine();

                        foreach (MemberChangeConflict col in changeConflict.MemberConflicts)
                        {
                            Console.WriteLine("Column name : {0}", col.Member.Name);
                            Console.WriteLine("Original value : {0}", col.OriginalValue);
                            Console.WriteLine("Current value :{0}", col.CurrentValue);
                            Console.WriteLine("Database value :{0}", col.DatabaseValue);
                            Console.WriteLine();
                        }
                    }
                }
            }
        }

        public static void Create(Cram cram)
        {
            using (var context = new eCramDataContext())
            {
                context.Crams.InsertOnSubmit(cram);

                context.SubmitChanges();
            }
        }



        public static void Delete(Cram cram)
        {
            using (var context = new eCramDataContext())
            {
                foreach (var cramLine in cram.CramLines)
                {
                    context.CramLines.DeleteOnSubmit(cramLine);
                }

                context.Crams.DeleteOnSubmit(cram);
                context.SubmitChanges();
            }
        }

        public static Cram GetById(Guid cramId)
        {
            using (var context = new eCramDataContext())
            {
                return (context.Crams.Where(c => c.Id == cramId)).Single();
            }
        }

        public static Cram FindCram(Guid userId, int month, int year)
        {
            return Search(year, month, userId, null, null).FirstOrDefault();
        }

        public static IEnumerable<Cram> FindCramsByUserId(Guid userId)
        {
            using (var context = new eCramDataContext())
            {
                return context.Crams.Where(c => c.UserId == userId)
                    .ToList();
            }
        }

        public static int GetCount()
        {
            using (var context = new eCramDataContext())
            {
                return context.Crams.Count();
            }
        }

        public static IEnumerable<Cram> GetTop(int start, int limit)
        {

            using (var context = new eCramDataContext())
            {
                return context.Crams
                    .Skip(start)
                    .Take(limit - start)
                    .ToList();
            }
        }

        public static IEnumerable<Cram> Search(int? year, int? month, Guid? userId, int? status, IEnumerable<Guid> listActivityId)
        {
            using (var context = new eCramDataContext())
            {
                var dlo = new DataLoadOptions();
                dlo.LoadWith<Cram>(c => c.User);
                dlo.LoadWith<Cram>(c => c.CramLines);
                dlo.LoadWith<Cram>(c => c.Cram_Attachments);
                dlo.LoadWith<CramLine>(c => c.Activity);
                dlo.LoadWith<CramLine>(c => c.Cram_DailyActivities);
                dlo.LoadWith<Activity>(c => c.ActivityType);
                dlo.LoadWith<Activity>(c => c.User_Activities);


                context.LoadOptions = dlo;



                Expression<Func<Cram, bool>> criterias = item => true;

                if (year.HasValue) criterias = criterias.MergeCriteria(item => item.Year == year.Value);
                if (month.HasValue) criterias = criterias.MergeCriteria(item => item.Month == month.Value);
                if (userId.HasValue) criterias = criterias.MergeCriteria(item => item.UserId == userId.Value);
                if (status.HasValue) criterias = criterias.MergeCriteria(GetStatusCriteria((CramStatus)status));
                if (listActivityId != null && listActivityId.Any()) criterias = criterias.MergeCriteria(item => item.CramLines.Any(line => listActivityId.Contains(line.ActivityId)));

                return context.Crams.Where(criterias).ToList();
            }
        }

        private static Expression<Func<Cram, bool>> GetStatusCriteria(CramStatus status)
        {
            switch (status)
            {
                case CramStatus.New:
                case CramStatus.Submit:
                case CramStatus.Validated:
                case CramStatus.Approved:
                case CramStatus.PartialApproved:
                    return item => item.CramLines.Any() && item.CramLines.All(line => line.Status == (int)status);
                case CramStatus.PartialValidated:
                    return item => item.CramLines.Any() && item.CramLines.All(line => line.Status == (int)CramStatus.Submit || line.Status == (int)CramStatus.Validated);

                default:
                    throw new ArgumentOutOfRangeException("status");
            }
        }

        public static IEnumerable<CramLine> GetCramsByActivityId(Guid activityId)
        {
            var context = new eCramDataContext();
            {
                var dlo = new DataLoadOptions();
                dlo.LoadWith<Cram>(c => c.User);
                dlo.LoadWith<Cram>(c => c.CramLines);
                dlo.LoadWith<Cram>(c => c.Cram_Attachments);
                dlo.LoadWith<CramLine>(c => c.Activity);
                dlo.LoadWith<CramLine>(c => c.Cram_DailyActivities);
                dlo.LoadWith<Activity>(c => c.ActivityType);


                return context.Crams.Where(c => c.CramLines.Any(line => line.ActivityId == activityId
                    //&& (line.Status == (int)CramStatus.Submit || line.Status == (int)CramStatus.Validated || line.Status == (int)CramStatus.PartialValidated)
                        ))
                   .Select(c => c.CramLines)
                   .FlatList()
                   .ToList();
            }
        }

        public static IEnumerable<CramLine> GetCramsToValid(Guid idUser)
        {
            using (var context = new eCramDataContext())
            {
                var dlo = new DataLoadOptions();
                dlo.LoadWith<Cram>(c => c.User);
                //dlo.LoadWith<Cram>(c => c.CramLines);
                dlo.LoadWith<CramLine>(c => c.Cram);
                dlo.LoadWith<CramLine>(c => c.Activity);
                dlo.LoadWith<CramLine>(c => c.Cram_DailyActivities);
                dlo.LoadWith<Activity>(c => c.ActivityType);
                context.LoadOptions = dlo;

                var user = context.Users.FirstOrDefault(u => u.Id == idUser);
                if (user == null)
                {
                    return new List<CramLine>();
                }

                var activityList = ActivityRepository.GetRestrictedActivityList(idUser).Select(a => a.Id);
                Expression<Func<CramLine, bool>> criterias = c => activityList.Contains(c.ActivityId);

                if (user.Role == (int)UserRole.Administrator)
                {
                    criterias = criterias.MergeCriteria(c => (c.Status == (int)CramStatus.Submit || c.Status == (int)CramStatus.Validated));
                }
                else if (user.Role == (int)UserRole.ProjectLeader)
                {
                    criterias = criterias.MergeCriteria(c => c.Status == (int)CramStatus.Submit);
                }

                return context.CramLines
                    .Where(criterias)
                    .ToList();
            }
        }

        public static Cram GetByCramLineId(Guid id)
        {
            using (var context = new eCramDataContext())
            {
                var cramLine = context.CramLines.Where(c => c.Id == id).FirstOrDefault();
                if (cramLine == null)
                    return null;

                return GetById(cramLine.CramId);
            }
        }

        public static IEnumerable<CramSummary> GetCramsSummary()
        {
            using (var context = new eCramDataContext())
            {
                var loadOption = new DataLoadOptions();
                loadOption.LoadWith<Cram>(c => c.CramLines);
                context.LoadOptions = loadOption;

                var cramLine = context.Crams
                    .ToList()
                    .GroupBy(c => new {c.Year, c.Month})
                    .Select(c => new CramSummary { Year = c.Key.Year, Month = c.Key.Month, 
                        NbNew = c.Count(i => i.Status == CramStatus.New),
                        NbSubmit = c.Count(i => i.Status == CramStatus.Submit),
                        NbPartialValidated = c.Count(i => i.Status == CramStatus.PartialValidated),
                        NbValidated = c.Count(i => i.Status == CramStatus.Validated),
                        NbPartialApproved= c.Count(i => i.Status == CramStatus.PartialApproved),
                        NbApproved = c.Count(i => i.Status == CramStatus.Approved)})
                    ;

                return cramLine;
            }
        }

        public static Cram_Attachment GetAttachmentByCramIdAndName(Guid idCram, string attachmentName)
        {
            using (var context = new eCramDataContext())
            {
                var loadOption = new DataLoadOptions();
                loadOption.LoadWith<Cram_Attachment>(c => c.Cram_AttachmentData);
                context.LoadOptions = loadOption;

                return context.Cram_Attachments
                    .FirstOrDefault(a => a.CramId == idCram && a.AttachmentName == attachmentName);
            }
        }
    }
}
